Package.php

<?php

namespace Tlf\User;

use Tlf\User\Configurations as C;

class Package extends \Lia\Package\Server {

    public string $fqn = 'taeluf:user';
    public string $name = 'user';

    public readonly \Tlf\User\Lib $lib;

    public readonly ?string $config_file;
    protected bool $use_risky_web_initialization = false;
    protected bool $enable_admin_dashboard = false;

    /** key is name to show to user. value is a view name */
    protected array $dashboard_items;

    public function __construct(\Lia $lia, \PDO $pdo, array $configs){
        $configs = array_merge(
            [ // defaults
                C::base_url => '/user/',
                C::web_address => null,
                C::email_from => null,
                C::disabled_pages => [],// One of 'login', 'register', 'reset-password', 'logout', or 'terms' 
                C::mail_service => \Tlf\User\MailService::PHP_MAIL->value,
                C::mail_service_callable => null,
            ],
            $configs
        );

        parent::__construct($lia,$this->fqn,dirname(__DIR__),$configs[C::base_url]);

        $lib = new \Tlf\User\Lib($pdo);
        $this->lib = $lib;

        $this->public_file_params = [
            'lib' => $lib,
        ];

        $lia->addMethod('addUserDashboard', [$this, 'add_user_dashboard']);

        $lia->hook(\Tlf\User\Hooks::DASHBOARD_WILL_DISPLAY, [$this, 'add_main_dashboards']);

        $this->update_configs($configs);
    }


    /** 
     * Get the html for a link within this library.
     * @throws if link name not set
     */
    public function link(string $link_name): string {
        $link = $this->lia->view('user:Links',['links'=>[$link_name]]);
        return $link."";
    }

    /** 
     * Get the html for a list of links within this lib.
     * @throws if link name not set
     */
    public function links(...$links): string {
        $link = $this->lia->view('user:Links',['links'=>[...$links]]);
        return $link."";
    }

    /**
     * Updates the configs, but does not change the base_url, which is configured during instantiation. 
     *
     * @param $configs array of configs. Only writes keys found in this array. If this configs array does not contain a key, it will not erase an existing config.
     */
    public function update_configs(array $configs){
        $lib = $this->lib;
        $lib->config = array_merge($lib->config, $configs);
        $configs = $lib->config;

        $lib->mail_service = \Tlf\User\MailService::from($configs[C::mail_service]);
        $lib->mail_service_callable = $configs[C::mail_service_callable];

        $lib->disabled_pages = [
            $configs[C::disabled_pages], 
        ];
    }


    /**
     * Load configs from the named config file. This config file will also be used by the risky_web_initialization() to store configs. Alternatively, just pass an array to the constructor of this class.
     *
     * If file does not exist, program continues as normal
     *
     * @param $json_path absolute path to a json file
     */
    public function use_config_file(string $json_path){
        $this->config_file = $json_path;

        if (file_exists($json_path)){
            $configs = json_decode(file_get_contents($json_path), true, 10, JSON_THROW_ON_ERROR);
            $this->update_configs($configs);
        } else if ($this->enable_admin_dashboard){
            error_log("Config file '$json_path' does not exist");
        } else {
            throw new \Exception("Config file '$json_path' does not exist. Either create the file or call 'enable_admin_dashboard()' prior to 'use_config_file()'");
        }
    }
    /**
     * The easiest way to initialize configs for your user library. This gives you a web page to input your mail server settings and to initialize the database.
     *
     * There are several security measures to prevent unauthorized access, BUT this should not be called in production BECAUSE it exposes administrative functionality that, if exploited, could cause real problems.
     *
     * You don't want some unexpected error or hack to let somebody change your mail server credentials. This could expose all your users to phishing attacks.
     *
     * So you should push to test or production, do the initial setup via the gui, then comment out the method call & push the updated code to the server after things are initialized. Be sure to delete the local initialization pin file before pushing changes.
     *
     * @param $initialization_pin_file absolute path to a .txt file that contains a pin that's 20-40 characters long
     */
    public function enable_risky_web_initialization(string $initialization_pin_file){


        $init_url = $this->url('/initialize/');
        if ($init_url !== $_SERVER['REQUEST_URI']){
            throw new \Exception("risky_web_initialization is active, so you CANNOT visit any page other than '$init_url' until you remove the call to 'enable_risky_web_initialization()'.");
        }

        if (!is_file($initialization_pin_file)){
            throw new \Exception("Initialization pin file does not exit. The file is automatically deleted when initialization is complete. If you've already initialized, then remove the call to 'enable_risky_web_initialization()'. Otherwise, create the file.");
        }


        $content = file_get_contents($initialization_pin_file);
        if (strlen($content)<20 || strlen($content) > 40){
            throw new \Exception("Initialization pin file must contain a string that is 20-40 characters long.");
        }

        error_log("WARNING: User Library Risy Web Initialization is enabled. This should NOT be enabled in production.");
        error_log("WARNING: User Library Risy Web Initialization is enabled. This should NOT be enabled in production.");
        error_log("WARNING: User Library Risy Web Initialization is enabled. This should NOT be enabled in production.");

        $this->use_risky_web_initialization = true;

        $this->public_file_params['initialization_pin_file'] = $initialization_pin_file;
        
        $init_addon = new \Tlf\User\InitializationAddon($this);
    }

    /**
     * Enables views for the admin dasbhoard, which allows configuration of the mail server & some other settings that would otherwise be configured through code.
     *
     */
    public function enable_admin_dashboard(){

        //if (!isset($this->config_file)){
            //throw new \Exception("You MUST call `use_config_file(string \$json_path)` prior to enabling the admin dashboard.");
        //}

        $this->enable_admin_dashboard = true;

        $init_addon = new \Tlf\User\AdminDashboardAddon($this);
    }

    /**
     * Get the current user.
     * If there is no active login, a user is returned that is neither registered nor logged in and is not in the database. 
     * @return \Tlf\User, whether there is a login or not.
     */
    public function get_user(): \Tlf\User {

        // log the user in  
        $current_user = $this->lib->user_from_cookie();  
        // if there was no cookie, we'll use an unregistered user  
        if ($current_user===false)$current_user = new \Tlf\User($this->lib->pdo);  

        //$this->public_file_params['user'] = $current_user; 
        
        return $current_user;
    }


    public function get_dashboards(){
        return $this->dashboard_items;
    }

    /** DASHBOARD_WILL_DISPLAY hook to setup dashboards */
    public function add_main_dashboards(){
        $lia = $this->lia;
        $this->add_user_dashboard('Main', 'user:dashboard/main');
        $this->add_user_dashboard('Your Permissions', 'user:dashboard/permissions',);
        $this->add_user_dashboard('Security Logs', 'user:dashboard/security_logs');
    }

    public function add_user_dashboard(string $name, string $view_name){
        $this->dashboard_items[$name] = $view_name;
    }

        
}